////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//                                                                                                                            //
//                                                         WNProject                                                          //
//                                                                                                                            //
//            This file is distributed under the BSD 2-Clause open source license. See License.txt for details.               //
//                                                                                                                            //
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

#pragma once

#ifndef __WN_MATH_X86_BASIC_INL__
#define __WN_MATH_X86_BASIC_INL__

#ifndef __WN_MATH_BASIC_H__
    #error "x86/WNBasic.inl should never be included directly. Please include WNBasic.h instead"
#elif !defined _WN_X86
    #error "x86/WNBasic.inl has been included on a non x86 platform. Please rectify this."
#elif !defined __WN_MATH_ENABLE_EXTENSIONS
    #error "x86/WNBasic.inl has been included when extensions are disabled. Please rectify this."
#endif

#ifdef __WN_MATH_SSE_ALLOWED
    #include "WNMath/inc/Internal/x86/SSE/WNBasic.inl"
#endif

#ifdef __WN_MATH_SSE2_ALLOWED
    #include "WNMath/inc/Internal/x86/SSE2/WNBasic.inl"
#endif

#ifdef __WN_MATH_SSE4_1_ALLOWED
    #include "WNMath/inc/Internal/x86/SSE4.1/WNBasic.inl"
#endif

#ifdef __WN_MATH_ALLOW_RUNTIME
    #include "WNCore/inc/WNSystem.h"
#endif

namespace WNMath {
    template <>
    WN_FORCE_INLINE WN_FLOAT32 WNMod(const WN_FLOAT32 _dividend, const WN_FLOAT32 _divisor) {
        #ifdef __WN_MATH_SSE4_1_FORCED
            return(__WNInternal::__WNModSSE4_1(_dividend, _divisor));
        #else
            #ifdef __WN_MATH_SSE4_1_ALLOWED
                if (WNCore::WNSystem::WNHasSSE41()) {
                    return(__WNInternal::__WNModSSE4_1(_dividend, _divisor));
                }
            #endif

            return(__WNInternal::__WNModGeneric(_dividend, _divisor));
        #endif
    }

    template <>
    WN_FORCE_INLINE WN_FLOAT64 WNMod(const WN_FLOAT64 _dividend, const WN_FLOAT64 _divisor) {
        #ifdef __WN_MATH_SSE4_1_FORCED
            return(__WNInternal::__WNModSSE4_1(_dividend, _divisor));
        #else
            #ifdef __WN_MATH_SSE4_1_ALLOWED
                if (WNCore::WNSystem::WNHasSSE41()) {
                    return(__WNInternal::__WNModSSE4_1(_dividend, _divisor));
                }
            #endif

            return(__WNInternal::__WNModGeneric(_dividend, _divisor));
        #endif
    }

    template <>
    WN_FORCE_INLINE WN_FLOAT32 WNRound(const WN_FLOAT32& _number) {
        #ifdef __WN_MATH_SSE4_1_FORCED
            return(__WNInternal::__WNRoundSSE4_1(_number));
        #else
            #ifdef __WN_MATH_SSE4_1_ALLOWED
                if (WNCore::WNSystem::WNHasSSE41()) {
                    return(__WNInternal::__WNRoundSSE4_1(_number));
                }
            #endif

            return(__WNInternal::__WNRoundGeneric(_number));
        #endif
    }

    template <>
    WN_FORCE_INLINE WN_FLOAT64 WNRound(const WN_FLOAT64& _number) {
        #ifdef __WN_MATH_SSE4_1_FORCED
            return(__WNInternal::__WNRoundSSE4_1(_number));
        #else
            #ifdef __WN_MATH_SSE4_1_ALLOWED
                if (WNCore::WNSystem::WNHasSSE41()) {
                    return(__WNInternal::__WNRoundSSE4_1(_number));
                }
            #endif

            return(__WNInternal::__WNRoundGeneric(_number));
        #endif
    }

    template <>
    WN_FORCE_INLINE WN_FLOAT32 WNCeil(const WN_FLOAT32& _number) {
        #ifdef __WN_MATH_SSE4_1_FORCED
            return(__WNInternal::__WNCeilSSE4_1(_number));
        #else
            #ifdef __WN_MATH_SSE4_1_ALLOWED
                if (WNCore::WNSystem::WNHasSSE41()) {
                    return(__WNInternal::__WNCeilSSE4_1(_number));
                }
            #endif

            return(__WNInternal::__WNCeilGeneric(_number));
        #endif
    }

    template <>
    WN_FORCE_INLINE WN_FLOAT64 WNCeil(const WN_FLOAT64& _number) {
        #ifdef __WN_MATH_SSE4_1_FORCED
            return(__WNInternal::__WNCeilSSE4_1(_number));
        #else
            #ifdef __WN_MATH_SSE4_1_ALLOWED
                if (WNCore::WNSystem::WNHasSSE41()) {
                    return(__WNInternal::__WNCeilSSE4_1(_number));
                }
            #endif

            return(__WNInternal::__WNCeilGeneric(_number));
        #endif
    }

    template <>
    WN_FORCE_INLINE WN_FLOAT32 WNFloor(const WN_FLOAT32& _number) {
        #ifdef __WN_MATH_SSE4_1_FORCED
            return(__WNInternal::__WNFloorSSE4_1(_number));
        #else
            #ifdef __WN_MATH_SSE4_1_ALLOWED
                if (WNCore::WNSystem::WNHasSSE41()) {
                    return(__WNInternal::__WNFloorSSE4_1(_number));
                }
            #endif

            return(__WNInternal::__WNFloorGeneric(_number));
        #endif
    }

    template <>
    WN_FORCE_INLINE WN_FLOAT64 WNFloor(const WN_FLOAT64& _number) {
        #ifdef __WN_MATH_SSE4_1_FORCED
            return(__WNInternal::__WNFloorSSE4_1(_number));
        #else
            #ifdef __WN_MATH_SSE4_1_ALLOWED
                if (WNCore::WNSystem::WNHasSSE41()) {
                    return(__WNInternal::__WNFloorSSE4_1(_number));
                }
            #endif

            return(__WNInternal::__WNFloorGeneric(_number));
        #endif
    }

    template <>
    WN_FORCE_INLINE WN_FLOAT32 WNTrunc(const WN_FLOAT32& _number) {
        #ifdef __WN_MATH_SSE4_1_FORCED
            return(__WNInternal::__WNTruncSSE4_1(_number));
        #else
            #ifdef __WN_MATH_SSE4_1_ALLOWED
                if (WNCore::WNSystem::WNHasSSE41()) {
                    return(__WNInternal::__WNTruncSSE4_1(_number));
                }
            #endif

            return(__WNInternal::__WNTruncGeneric(_number));
        #endif
    }

    template <>
    WN_FORCE_INLINE WN_FLOAT64 WNTrunc(const WN_FLOAT64& _number) {
        #ifdef __WN_MATH_SSE4_1_FORCED
            return(__WNInternal::__WNTruncSSE4_1(_number));
        #else
            #ifdef __WN_MATH_SSE4_1_ALLOWED
                if (WNCore::WNSystem::WNHasSSE41()) {
                    return(__WNInternal::__WNTruncSSE4_1(_number));
                }
            #endif

            return(__WNInternal::__WNTruncGeneric(_number));
        #endif
    }

    template <>
    WN_FORCE_INLINE WN_FLOAT32 WNSqrt(const WN_FLOAT32& _number) {
        #ifdef __WN_MATH_SSE_FORCED
            return(__WNInternal::__WNSqrtSSE(_number));
        #else
            #ifdef __WN_MATH_SSE_ALLOWED
                if (WNCore::WNSystem::WNHasSSE()) {
                    return(__WNInternal::__WNSqrtSSE(_number));
                }
            #endif

            return(__WNInternal::__WNSqrtGeneric(_number));
        #endif
    }

    template <>
    WN_FORCE_INLINE WN_FLOAT64 WNSqrt(const WN_FLOAT64& _number) {
        #ifdef __WN_MATH_SSE2_FORCED
            return(__WNInternal::__WNSqrtSSE2(_number));
        #else
            #ifdef __WN_MATH_SSE2_ALLOWED
                if (WNCore::WNSystem::WNHasSSE2()) {
                    return(__WNInternal::__WNSqrtSSE2(_number));
                }
            #endif

            return(__WNInternal::__WNSqrtGeneric(_number));
        #endif
    }

    template <>
    WN_FORCE_INLINE WN_FLOAT32 WNInvSqrt(const WN_FLOAT32& _number) {
        #ifdef __WN_MATH_SSE_FORCED
            return(__WNInternal::__WNInvSqrtSSE(_number));
        #else
            #ifdef __WN_MATH_SSE_ALLOWED
                if (WNCore::WNSystem::WNHasSSE()) {
                    return(__WNInternal::__WNInvSqrtSSE(_number));
                }
            #endif

            return(__WNInternal::__WNInvSqrtGeneric(_number));
        #endif
    }

    template <>
    WN_FORCE_INLINE WN_FLOAT64 WNInvSqrt(const WN_FLOAT64& _number) {
        #ifdef __WN_MATH_SSE2_FORCED
            return(__WNInternal::__WNInvSqrtSSE2(_number));
        #else
            #ifdef __WN_MATH_SSE2_ALLOWED
                if (WNCore::WNSystem::WNHasSSE2()) {
                    return(__WNInternal::__WNInvSqrtSSE2(_number));
                }
            #endif

            return(__WNInternal::__WNInvSqrtGeneric(_number));
        #endif
    }

    template <>
    WN_FORCE_INLINE WN_FLOAT32 WNMin(const WN_FLOAT32& _first, const WN_FLOAT32& _second) {
        #ifdef __WN_MATH_SSE_FORCED
            return(__WNInternal::__WNMinSSE(_first, _second));
        #else
            #ifdef __WN_MATH_SSE_ALLOWED
                if (WNCore::WNSystem::WNHasSSE()) {
                    return(__WNInternal::__WNMinSSE(_first, _second));
                }
            #endif

            return(__WNInternal::__WNMinGeneric(_first, _second));
        #endif
    }

    template <>
    WN_FORCE_INLINE WN_FLOAT64 WNMin(const WN_FLOAT64& _first, const WN_FLOAT64& _second) {
        #ifdef __WN_MATH_SSE2_FORCED
            return(__WNInternal::__WNMinSSE2(_first, _second));
        #else
            #ifdef __WN_MATH_SSE2_ALLOWED
                if (WNCore::WNSystem::WNHasSSE2()) {
                    return(__WNInternal::__WNMinSSE2(_first, _second));
                }
            #endif

            return(__WNInternal::__WNMinGeneric(_first, _second));
        #endif
    }

    template <>
    WN_FORCE_INLINE WN_FLOAT32 WNMax(const WN_FLOAT32& _first, const WN_FLOAT32& _second) {
        #ifdef __WN_MATH_SSE_FORCED
            return(__WNInternal::__WNMaxSSE(_first, _second));
        #else
            #ifdef __WN_MATH_SSE_ALLOWED
                if (WNCore::WNSystem::WNHasSSE()) {
                    return(__WNInternal::__WNMaxSSE(_first, _second));
                }
            #endif

            return(__WNInternal::__WNMaxGeneric(_first, _second));
        #endif
    }

    template <>
    WN_FORCE_INLINE WN_FLOAT64 WNMax(const WN_FLOAT64& _first, const WN_FLOAT64& _second) {
        #ifdef __WN_MATH_SSE2_FORCED
            return(__WNInternal::__WNMaxSSE2(_first, _second));
        #else
            #ifdef __WN_MATH_SSE2_ALLOWED
                if (WNCore::WNSystem::WNHasSSE2()) {
                    return(__WNInternal::__WNMaxSSE2(_first, _second));
                }
            #endif

            return(__WNInternal::__WNMaxGeneric(_first, _second));
        #endif
    }

    template <>
    WN_FORCE_INLINE WN_FLOAT32 WNClamp(const WN_FLOAT32& _number, const WN_FLOAT32& _min, const WN_FLOAT32& _max) {
        #ifdef __WN_MATH_SSE_FORCED
            return(__WNInternal::__WNClampSSE(_number, _min, _max));
        #else
            #ifdef __WN_MATH_SSE_ALLOWED
                if (WNCore::WNSystem::WNHasSSE()) {
                    return(__WNInternal::__WNClampSSE(_number, _min, _max));
                }
            #endif

            return(__WNInternal::__WNClampGeneric(_number, _min, _max));
        #endif
    }

    template <>
    WN_FORCE_INLINE WN_FLOAT64 WNClamp(const WN_FLOAT64& _number, const WN_FLOAT64& _min, const WN_FLOAT64& _max) {
        #ifdef __WN_MATH_SSE2_FORCED
            return(__WNInternal::__WNClampSSE2(_number, _min, _max));
        #else
            #ifdef __WN_MATH_SSE2_ALLOWED
                if (WNCore::WNSystem::WNHasSSE2()) {
                    return(__WNInternal::__WNClampSSE2(_number, _min, _max));
                }
            #endif

            return(__WNInternal::__WNClampGeneric(_number, _min, _max));
        #endif
    }
}

#endif // __WN_MATH_X86_BASIC_INL__